
/*************************************************************************
* Copyright (c) 2008, ShenZhen Coship Electronic Ltd Co.
* All rights reserved.
* 
* File Name:		MPEG4Codec.cpp
* Summary:			implementation of the CMPEG4Codec class.
* 
* Current Version:	1.0
* Author(s):
/------------------------------------------------------------------------/
revision			author				reason					date
1.0					Ding Ning			Initialize Version		2008-6-18
/------------------------------------------------------------------------/
*************************************************************************/

// MPEG4Codec.cpp: implementation of the CMPEG4Codec class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "MPEG4Codec.h"
//yxy
//#include "VideoOutputDevice.h"
//#include "VideoInputDevice.h"
void RGBtoYUV420P(const BYTE * rgb,
				   BYTE * yuv,
				   int w,int h,
				   unsigned rgbIncrement,
				   BOOL flip);
/*
#ifndef READ_PNM
#define IMAGE_SIZE(x,y) ((x)*(y)*3/2)
#else
#define IMAGE_SIZE(x,y) ((x)*(y)*3)
#endif
*/
#define MAX(A,B) ( ((A)>(B)) ? (A) : (B) )
#define SMALL_EPS (1e-10)

#define SWAP(a) ( (((a)&0x000000ff)<<24) | (((a)&0x0000ff00)<<8) | \
                  (((a)&0x00ff0000)>>8)  | (((a)&0xff000000)>>24) )



//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CMPEG4Codec::CMPEG4Codec(unsigned w,unsigned h,Direction dir)//:CVideoCodec(w,h,dir)
{
	m_eDirection = dir;
	m_bIsOpened = FALSE;
    //yxy
	//m_bShowBitmap = FALSE;
	m_hCodecDll = NULL;
	m_pDriverProc = NULL;
	m_nBitRate = 320;
	m_nBitRateHighLimit = 0;
	m_nFramesPerSec = 0;
	m_nQ = 27;

	//yxy
	m_nFrameWidth = w;
	m_nFrameHeight = h;

	memset(&m_BIHeader,0,sizeof(m_BIHeader));

	m_BIHeader.biSize = sizeof(BITMAPINFOHEADER);
	m_BIHeader.biWidth = w;
	m_BIHeader.biHeight = h;
	m_BIHeader.biPlanes = 1;
	m_BIHeader.biSizeImage = w*h*3;	
	m_BIHeader.biBitCount = 24;
	m_BIHeader.biCompression = BI_RGB;

	m_nFrames = 0;
	if (dir == Encoder)
	{
		m_nBufferLen = w*h*4;
		m_pBuffer = new BYTE[m_nBufferLen];
	}
	else
	{
		m_nBufferLen = 0;
		m_pBuffer = 0;
	}
	if(dir == Encoder)
	{
		m_pYUVBuffer = new BYTE[w*h + w*h];
	}
	else
		m_pYUVBuffer = NULL;
    //yxy
	//m_pInputDevice = NULL;
	//m_pOutputDevice = NULL;
	InitializeCriticalSection(&m_CriticalSection);
	///m_pBitmapFile = new CBitmapFile;
	///m_pBitmapFile->SetFrameSize(w,h);
}

CMPEG4Codec::~CMPEG4Codec()
{
	Close();
	DeleteCriticalSection(&m_CriticalSection);
	///delete m_pBitmapFile;
}

//yxy
BOOL CMPEG4Codec::Write(const char *pInputbuf,unsigned len,BOOL key, char *pOutBuff)
{		
	unsigned inc = 0;
	if(m_pDriverProc == NULL)
		return FALSE;

	ICDECOMPRESS decompress;
	decompress.dwFlags = 0;
	decompress.ckid = 0;
	m_bihIn.biSizeImage = len;
//	m_bihOut.biWidth = m_nFrameWidth;
//	m_bihOut.biHeight = m_nFrameHeight;
	m_bihOut.biSizeImage = m_nFrameWidth*m_nFrameHeight*3;
	decompress.lpbiInput = &m_bihIn;
	decompress.lpbiOutput = &m_bihOut;
	decompress.lpInput = (void *)pInputbuf;
	decompress.lpOutput = (void *)pOutBuff;

	LONG Ret = m_pDriverProc(m_pInst,NULL,ICM_DECOMPRESS ,(LPARAM)&decompress, (LPARAM)sizeof(ICDECOMPRESS));

//	if(Ret)
//		return FALSE;
	return (!Ret);

	/*
	while(inc < len)
	{
	//	used_bytes = dec_main((BYTE*)&pbuf[inc], (BYTE*)m_pBuffer, len - inc , &xvid_dec_stats);
		if(xvid_dec_stats.data.vol.width > 0 && xvid_dec_stats.data.vol.height > 0)
		{
			if(xvid_dec_stats.data.vol.width != m_nFrameWidth || xvid_dec_stats.data.vol.height != m_nFrameHeight)
			{	
				m_nFrameWidth = xvid_dec_stats.data.vol.width;
				m_nFrameHeight = xvid_dec_stats.data.vol.height;		
				if(m_pBuffer)
					delete[] m_pBuffer;
				m_nBufferLen = m_nFrameWidth*m_nFrameHeight*3;
				m_pBuffer = new BYTE[m_nBufferLen];
				memset(&m_BIHeader,0,sizeof(m_BIHeader));
				m_BIHeader.biSize = sizeof(BITMAPINFOHEADER);
				m_BIHeader.biWidth = m_nFrameWidth;
				m_BIHeader.biHeight = m_nFrameHeight;
				m_BIHeader.biPlanes = 1;
				m_BIHeader.biSizeImage = m_nFrameWidth*m_nFrameHeight*3;	
				m_BIHeader.biBitCount = 24;
				m_BIHeader.biCompression = BI_RGB;
				m_nFrames = 0;

				//SetSize(xvid_dec_stats.data.vol.width,xvid_dec_stats.data.vol.height);
				//return TRUE;
			}
		}
		inc+=used_bytes;
	}
	*/
	if(m_bihOut.biWidth > 0 && m_bihOut.biHeight > 0)
	{
	//	if(xvid_dec_stats.data.vol.width != m_nFrameWidth || xvid_dec_stats.data.vol.height != m_nFrameHeight)
		{	
		//	SetSize(xvid_dec_stats.data.vol.width,xvid_dec_stats.data.vol.height);
		}
	//	else
		{
//			if(m_pOutputDevice)
//			{
//				uint8 *src[4];
//				src[0] = m_pBuffer;
//				int strid[4];
//				strid[0] = m_bihOut.biWidth*m_bihOut.biHeight*3;
//				strid[1] = strid[2] = strid[3] = 0;
//				((CVideoOutputDevice *)m_pOutputDevice)->SetFrameSize(m_bihOut.biWidth,m_bihOut.biHeight);
//				m_pOutputDevice->Write(src,strid,VIDEO_CSP_RGB);
//			}
			return TRUE;
		}
	}	
	return FALSE;
}

BOOL CMPEG4Codec::Read(char *pdata,unsigned len,unsigned &readed,int &key,unsigned w,unsigned h,const char *bData)
{
	unsigned int  datalen = 0;
	BOOL res = FALSE;
    //yxy
	//unsigned w,h;
	readed = 0;
	//SetSize(m_pInputDevice->GetFrameWidth(),m_pInputDevice->GetFrameHeight());

	EnterCriticalSection(&m_CriticalSection);
	if(m_bIsOpened == FALSE)
	{
		LeaveCriticalSection(&m_CriticalSection);
		return res;
	}
    //yxy
    /*
	if(m_bShowBitmap && m_pBitmapFile)
	{
		w = m_pBitmapFile->GetFrameWidth();
		h = m_pBitmapFile->GetFrameHeight();
	}
	else if(m_pInputDevice)
	{
		w = ((CVideoInputDevice *)m_pInputDevice)->GetFrameWidth();
		h = ((CVideoInputDevice *)m_pInputDevice)->GetFrameHeight();
	
	}
	else
	{
		LeaveCriticalSection(&m_CriticalSection);
		return res;
	}
	*/
	if(w != m_nFrameWidth || h != m_nFrameHeight)
	{
		SetFrameSize(w,h);
	}
    //yxy
    /*
	if((m_bShowBitmap && m_pBitmapFile->Read(m_pBuffer,m_nBufferLen,datalen)) || 
		(m_pInputDevice && m_pInputDevice->Read(m_pBuffer,m_nBufferLen,datalen)))
	{
	*/
	datalen = w*h*3;
	memcpy(m_pBuffer,bData,datalen);
	//end
		if(w != m_nFrameWidth || h != m_nFrameHeight)
		{
			SetFrameSize(w,h);
		}
		else if(datalen > 0 )
		{
			DWORD dwFlags;
			DWORD dwckid;
			ICCOMPRESS compress;
			compress.dwFlags = ICCOMPRESS_KEYFRAME ;
			compress.dwFrameSize = 0;
			compress.dwQuality = 6;
			compress.lFrameNum = 1;
			compress.lpbiInput = &m_bihIn;
			compress.lpbiOutput = &m_bihOut;
			compress.lpbiPrev = &m_bihIn;
			compress.lpckid = &dwckid;
			compress.lpdwFlags = &dwFlags;
			compress.lpInput = m_pBuffer;
			compress.lpOutput = pdata;
			compress.lpPrev = NULL;//m_pBuffer;
			LONG Ret = m_pDriverProc(m_pInst,NULL,ICM_COMPRESS ,(LPARAM)&compress, (LPARAM)sizeof(ICCOMPRESS));
			readed = m_bihOut.biSizeImage;
			key = dwFlags & AVIIF_KEYFRAME;	
	
			m_nFrames++;
			res = TRUE;
            //yxy
            /*
			if(m_pOutputDevice)
			{
				uint8 *src[4];
				src[0] = m_pBuffer;
				int strid[4];
				strid[0] = datalen;
				strid[1] = strid[2] = strid[3] = 0;
				((CVideoOutputDevice *)m_pOutputDevice)->SetFrameSize(m_bihOut.biWidth,m_bihOut.biHeight);
							
				m_pOutputDevice->Write(src,strid,VIDEO_CSP_RGB);
			}
			*/
		}	
	LeaveCriticalSection(&m_CriticalSection);
	return res;
}
BOOL CMPEG4Codec::Open()
{
	if(m_bIsOpened)
		return FALSE;
	Close();

/*
	char path_buffer[_MAX_PATH];
	char drive[_MAX_DRIVE];
	char dir[_MAX_DIR];
	char fname[_MAX_FNAME];
	char ext[_MAX_EXT];

	char activexdir[_MAX_PATH];

	::GetModuleFileName((HMODULE)this, path_buffer, _MAX_PATH);
	_splitpath( path_buffer, drive, dir, fname, ext );
	sprintf(activexdir,"%s%svizbcodec.dll",drive,dir);
*/
//    TCHAR activexdir[_MAX_PATH];
//	char dlldir[_MAX_PATH];
//	SHGetSpecialFolderPath(NULL, // HWND
//		activexdir,
//		0x0024,
//		NULL);
//	sprintf(dlldir,"%s\\vizbcodec.dll",activexdir);
	m_hCodecDll = LoadLibrary("vizbcodec.dll");

	if(m_hCodecDll != NULL) 
	{
		if(m_eDirection == Decoder)
		{
			m_pDriverProc = (DriverProc) GetProcAddress(m_hCodecDll, "DriverProc");	

			if(m_pDriverProc == NULL) 
			{
				return FALSE;
			}		
				///** DRV_LOAD ** load the driver
			m_pDriverProc(0, NULL, DRV_LOAD, 0, 0);
		
			//** DRV_OPEN **
			// initialize the m_icOpen structure
			m_icOpen.dwSize = sizeof(m_icOpen);
			m_icOpen.fccType = ICTYPE_VIDEO;
			m_icOpen.fccHandler = mmioFOURCC('D','I','V','X');
			m_icOpen.dwFlags = ICMODE_DECOMPRESS ;//ICMODE_QUERY;

			// send the DRV_OPEN message
			// although the value returned is a pointer to a structure, we can store
			// it in a DWORD structure for simplicity
			m_pInst = m_pDriverProc(0, NULL, DRV_OPEN, 0, (DWORD)&m_icOpen);
			if(!m_pInst)
			{
				// an error has occured
				return FALSE; // error
			}

			//** ICM_COMPRESS_QUERY ** 
		
	
		
			// initialize lpbi1 and lpbi2
			m_bihOut.biSize = m_bihIn.biSize = sizeof(BITMAPINFOHEADER); 
			m_bihOut.biWidth = m_bihIn.biWidth = m_nFrameWidth;
			m_bihOut.biHeight = m_bihIn.biHeight = m_nFrameHeight; 
			m_bihOut.biPlanes = m_bihIn.biPlanes= 1; 
			m_bihOut.biCompression = BI_RGB;        // standard RGB bitmap for input 

			m_bihIn.biCompression = 1482049860;//mmioFOURCC('d','i','v','x');;      // 8-bit RLE for output format 
			m_bihOut.biBitCount = m_bihIn.biBitCount = 24;  // 8 bits-per-pixel format 
			m_bihOut.biSizeImage = m_bihIn.biSizeImage = m_nFrameWidth*m_nFrameHeight*3; 
			m_bihOut.biXPelsPerMeter = m_bihOut.biYPelsPerMeter = 
				m_bihIn.biXPelsPerMeter = m_bihIn.biYPelsPerMeter = 0; 
			m_bihOut.biClrUsed = m_bihOut.biClrImportant = 
				m_bihIn.biClrUsed = m_bihIn.biClrImportant = 0; 

			/*
			// send the ICM_COMPRESS_QUERY
			int statesize;
			statesize = m_pDriverProc(m_pInst,NULL,ICM_GETSTATE,NULL,0);
			char *state = new char[statesize];
			m_pDriverProc(m_pInst,NULL,ICM_GETSTATE,(LPARAM)state,(LPARAM)statesize);
			STATE_DATA *pstate = (STATE_DATA *)state;
			delete[] state;
			LONG lRet = m_pDriverProc(m_pInst, NULL, ICM_COMPRESS_QUERY, (LPARAM)&m_bihIn, (LPARAM)&m_bihOut);
			*/
			ICDECOMPRESSEX decompressex;
			decompressex.dwFlags = 0 ;
			decompressex.lpbiSrc = &m_bihIn;
			decompressex.lpbiDst = &m_bihOut;
			decompressex.lpSrc = NULL;//(void *)pbuf;
			decompressex.lpDst = NULL;//(void *)m_pBuffer;
			decompressex.dxSrc = decompressex.dxDst = m_nFrameWidth;
			decompressex.dySrc = decompressex.dyDst = m_nFrameHeight;
			decompressex.xSrc = decompressex.xDst = 0;
			decompressex.ySrc = decompressex.yDst = 0;

		//	LONG lRet = m_pDriverProc(m_pInst,NULL,ICM_DECOMPRESSEX_BEGIN,(LPARAM)&decompressex, (LPARAM)sizeof(ICDECOMPRESSEX));
			LONG lRet = m_pDriverProc(m_pInst,NULL,ICM_DECOMPRESS_BEGIN,(LPARAM)&m_bihIn, (LPARAM)&m_bihOut);


			m_bIsOpened = TRUE;
			return TRUE;
		}
		else
		{
			m_pDriverProc = (DriverProc) GetProcAddress(m_hCodecDll, "DriverProc");	

			if(m_pDriverProc == NULL) 
			{
				//	MP_ERROR("Can't find the \"decore\" entry point in DivX.dll. Please Install the latest DivX4Windows Package");				
				//decaps= NULL;
				return FALSE;
			}		

			///** DRV_LOAD ** load the driver
			m_pDriverProc(0, NULL, DRV_LOAD, 0, 0);

			//** DRV_OPEN **
			// initialize the m_icOpen structure
			m_icOpen.dwSize = sizeof(m_icOpen);
			m_icOpen.fccType = ICTYPE_VIDEO;
			m_icOpen.fccHandler = mmioFOURCC('D','I','V','X');
			m_icOpen.dwFlags = ICMODE_COMPRESS ;//ICMODE_QUERY;

			// send the DRV_OPEN message
			// although the value returned is a pointer to a structure, we can store
			// it in a DWORD structure for simplicity
			m_pInst = m_pDriverProc(0, NULL, DRV_OPEN, 0, (DWORD)&m_icOpen);
			if(!m_pInst)
			{
				// an error has occured
				return FALSE; // error
			}

			//** ICM_COMPRESS_QUERY ** 
		
	
		
			// initialize lpbi1 and lpbi2
			m_bihIn.biSize = m_bihOut.biSize = sizeof(BITMAPINFOHEADER); 
			m_bihIn.biWidth = m_bihOut.biWidth = m_nFrameWidth;
			m_bihIn.biHeight = m_bihOut.biHeight = m_nFrameHeight; 
			m_bihIn.biPlanes = m_bihOut.biPlanes= 1; 
			m_bihIn.biCompression = BI_RGB;        // standard RGB bitmap for input 
			m_bihOut.biCompression = 1482049860;//mmioFOURCC('d','i','v','x');;      // 8-bit RLE for output format 
			m_bihIn.biBitCount = m_bihOut.biBitCount = 24;  // 8 bits-per-pixel format 
			m_bihIn.biSizeImage = m_bihOut.biSizeImage = 0; 
			m_bihIn.biXPelsPerMeter = m_bihIn.biYPelsPerMeter = 
				m_bihOut.biXPelsPerMeter = m_bihOut.biYPelsPerMeter = 0; 
			m_bihIn.biClrUsed = m_bihIn.biClrImportant = 
				m_bihOut.biClrUsed = m_bihOut.biClrImportant = 0; 


			// send the ICM_COMPRESS_QUERY
			int statesize;
			statesize = m_pDriverProc(m_pInst,NULL,ICM_GETSTATE,NULL,0);
			char *state = new char[statesize];
			m_pDriverProc(m_pInst,NULL,ICM_GETSTATE,(LPARAM)state,(LPARAM)statesize);
			CONFIG *pstate = (CONFIG *)state;
			pstate->max_key_interval = 60;
			pstate->display_status = FALSE;
			m_pDriverProc(m_pInst,NULL,ICM_SETSTATE,(LPARAM)state,(LPARAM)statesize);
			delete[] state;
			state = NULL;
			LONG lRet = m_pDriverProc(m_pInst, NULL, ICM_COMPRESS_QUERY, (LPARAM)&m_bihIn, (LPARAM)&m_bihOut);
			lRet = m_pDriverProc(m_pInst,NULL,ICM_COMPRESS_BEGIN,(LPARAM)&m_bihIn, (LPARAM)&m_bihOut);
			m_bIsOpened = TRUE;
			return TRUE;
		}

	}
	return FALSE;	
}
BOOL CMPEG4Codec::Close()
{

	if(m_bIsOpened == FALSE)
		return TRUE;
	EnterCriticalSection(&m_CriticalSection);
	if(m_hCodecDll) 
	{	
		if(m_pInst)
		{
			if(m_eDirection == Encoder)
				m_pDriverProc(m_pInst,NULL,ICM_COMPRESS_END ,0,0);
			else
				m_pDriverProc(m_pInst,NULL,ICM_DECOMPRESS_END ,0,0);
			//** DRV_CLOSE
			m_pDriverProc(m_pInst, NULL, DRV_CLOSE, 0, (DWORD)&m_icOpen);
			//** DRV_FREE **
			m_pDriverProc(0, NULL, DRV_FREE, 0, 0);

		}

		FreeLibrary(m_hCodecDll);

		m_hCodecDll       = NULL;
		m_pDriverProc = NULL;
	}

	SAFE_DELETE(m_pYUVBuffer);
	SAFE_DELETE(m_pBuffer);
	m_nBufferLen = 0;
	m_bIsOpened = FALSE;
	LeaveCriticalSection(&m_CriticalSection);
	return TRUE;
}

BOOL CMPEG4Codec::SetFrameSize(unsigned w, unsigned h)
{
	if(w != m_nFrameWidth || h != m_nFrameHeight)
	{
		BOOL open = m_bIsOpened;
		if(open)
			Close();
		m_nFrameWidth = w,m_nFrameHeight = h;
		memset(&m_BIHeader,0,sizeof(m_BIHeader));
		m_BIHeader.biSize = sizeof(BITMAPINFOHEADER);
		m_BIHeader.biWidth = w;
		m_BIHeader.biHeight = h;
		m_BIHeader.biPlanes = 1;
		m_BIHeader.biSizeImage = w*h*3;	
		m_BIHeader.biBitCount = 24;
		m_BIHeader.biCompression = BI_RGB;
		m_nFrames = 0;
		m_nBufferLen = w*h*4;
        //yxy
		SAFE_DELETE(m_pBuffer);
        //end
		m_pBuffer = new BYTE[m_nBufferLen];
		if(m_eDirection == Encoder)
		{
			m_pYUVBuffer = new BYTE[w*h + w*h];
		}
		else
			m_pYUVBuffer = NULL;
        //yxy
		/*if(m_pBitmapFile)
			m_pBitmapFile->SetFrameSize(w,h);*/
		if(open)
			Open();
	}
	return TRUE;
}


//yxy
#if 0
BOOL CMPEG4Codec::SetBitmapFile(BOOL usebmpfile,const char *pfilename)
{
	if(usebmpfile && pfilename == NULL)
		return FALSE;
	
	EnterCriticalSection(&m_CriticalSection);
//	SetSize(320,540);
	if(usebmpfile)
	{
	
		if(m_pBitmapFile->ChangeBMPFile(pfilename))
		{
			m_bShowBitmap = TRUE;
		}
		else
			m_bShowBitmap = FALSE;
	}
	else
		m_bShowBitmap = FALSE;

	LeaveCriticalSection(&m_CriticalSection);
	return TRUE;
}
#endif
